﻿using Nemerle;
using Nemerle.Assertions;
using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;
using Nemerle2.Compiler;
using Microsoft.Cci;
using System.Diagnostics;

namespace MetadataReader.CCI
{
  [DebuggerDisplay("Name = {Name}")]
  class CciTypeInfo : NTypeInfo
  {
    private _typeDef  : INamedTypeDefinition;
    internal InternedKey : uint;

    /// <summary>Initializes a new instance of the <c>CciTypeInfo</c> class.</summary>
    public this(
      [NotNull] provider : CciMetadataProvider,
      [NotNull] typeDef  : INamedTypeDefinition,
      [NotNull] assembly : CciReferencedAssembly)
    {
      _typeDef    = typeDef;
      Provider    = provider;
      InternedKey = typeDef.InternedKey;
      Assembly    = assembly;
    }

    public override UniqueKey : long { get { InternedKey :> int } }

    public override IsObsolete : bool
    {
      get { Definition.Attributes.HasAttribute(Constants.ObsoleteAttributeFullName) }
    }

    protected Definition : INamedTypeDefinition { get { _typeDef } }

    public sealed override Assembly : IReferencedAssembly { get; }

    public sealed override CustomAttributes : ICustomAttributes
    {
      [Memoize] get { CciCustomAttributes(Provider, _typeDef.Attributes) }
    }

    public override IsAbstract : bool { get { _typeDef.IsAbstract } }
    public override IsStatic   : bool { get { _typeDef.IsStatic } }

    public override DeclaringType : ITypeInfo
    {
      [Memoize]
      get
      {
        match(_typeDef)
        {
          | ty is INestedTypeReference  => Provider.GetTypeInfo(ty.ContainingType)
          | _                           => null
        }
      }
    }

    internal        Provider      : CciMetadataProvider   { get; private set; }
    private static  _emptyNesteds : array[CciTypeInfo] = array[];
    mutable private _nesteds      : array[CciTypeInfo] = _emptyNesteds;

    // Properties

    public override Name     : string { get { _typeDef.Name.Value } }
    public override FullName : string { get { Utils.GetFullName(_typeDef) } }

    public override Accessibility : Accessibility
    {
      get
      {
        match (TypeHelper.TypeVisibilityAsTypeMemberVisibility(_typeDef))
        {
          | Assembly           => Accessibility.Internal;
          | Family             => Accessibility.Protected;
          | FamilyAndAssembly  => Accessibility.ProtectedAndInternal;
          | FamilyOrAssembly   => Accessibility.ProtectedOrInternal;
          | Public             => Accessibility.Public;
          | Private            => Accessibility.Private
          | Default | Mask
          | Other              => assert(false, "unknown accessibility");
        }
      }
    }

    public override AttributeTargets    : AttributeTargets  { get { throw NotImplementedException()} }
    public override DefaultIndexerName  : string            { get { throw NotImplementedException()} }
    public override IsDelegate          : bool              { get { _typeDef.IsDelegate } }
    public override IsEnum              : bool              { get { _typeDef.IsEnum } }

    public override IsFloating : bool
    {
      get
      {
        match (_typeDef.TypeCode)
        {
          | Float32 | Float64 => true
          | _ => false
        }
      }
    }

    public override IsInterface : bool { get { _typeDef.IsInterface } }
    public override IsModule    : bool { get { _typeDef.IsStatic } }

    public override IsNumeric : bool
    {
      get
      {
        match (_typeDef.TypeCode)
        {
          | Float32 | Float64 | Int8 | Int16 | Int32 | Int64 | UInt8 | UInt16 | UInt32 | UInt64 => true
          | _ => false
        }
      }
    }

    public override IsPrimitive : bool
    {
      get
      {
        match (_typeDef.TypeCode)
        {
          | NotPrimitive  => false
          | Invalid       => assert(false)
          | _             => true
        }
      }
    }

    public override IsSealed              : bool        { get { _typeDef.IsSealed } }
    public override IsValueType           : bool        { get { _typeDef.IsValueType } }
    public override IsUnsigned            : bool        { get { throw NotImplementedException()} }
    public override LookupMemberAvailable : bool        { get { throw NotImplementedException()} }

    /// <summary>
    /// Gets the underlying type of the enumeration
    /// </summary>
    public override UnderlyingType        : ValueOption[ITypeInfo]
    {
      get
      {
        def ut = _typeDef.UnderlyingType;

        if(ut.InternedKey == 0)
          ValueOption.None()
        else
          ValueOption.Some(Provider.GetTypeInfo(ut))
      }
    }


    // Methods

    public override CompareTo(other : ITypeInfo) : int
    {
       UniqueKey.CompareTo(other.UniqueKey)     
    }

    public override Equals(t : IMemberInfo) : bool
    {
      UniqueKey == t.UniqueKey
    }

    public override Equals(other : ITypeInfo) : bool
    {
      UniqueKey == other.UniqueKey      
    } 

    //public override GetExtensionPattern(name : string) : option[ExtensionPattern];

    public override GetHashCode() : int
    {
      UniqueKey.GetHashCode()      
    }

    internal SetNestedTypes(nesteds : array[CciTypeInfo]) : void
    {
      _nesteds = nesteds;
    }

    public override GetNestedTypes() : Seq[ITypeInfo]
    {
      _nesteds
    }

    [Memoize(Synchronized = true)]
    public override GetMembers() : Seq[IMemberInfo]
    {
      mutable members = array(_typeDef.Members.Count());
      mutable idx = 0;
      foreach(definition in _typeDef.Members)
      {
         members[idx] =
            match(definition)
            {
              | m is IPropertyDefinition  => CciPropertyInfo(this, m, idx)
              | m is IFieldDefinition     => CciFieldInfo   (this, m, idx)
              | m is IMethodDefinition    => CciMethodInfo  (this, m, idx)
              | m is IEventDefinition     => CciEventInfo   (this, m, idx)
              | m is ITypeDefinition      => Provider.GetTypeInfo(m)
              | _                         => null
            }
          idx++;
      }

      members
    }

    public override SelfType : FixedType.Class
    {
      get { throw NotImplementedException() }
    }
    
    public override GetDirectSuperTypes() : Seq[FixedType.Class]
    {
      throw NotImplementedException()
    }
    
    //_Eter_ TODO: перенести в базовый класс
    public override GetSuperTypes() : Seq[FixedType.Class] // все базовые классы (рекурсивно)
    {
      GetDirectSuperTypes().Union(GetDirectSuperTypes().SelectMany(t => t.info.GetSuperTypes()))      
    }
    public override HasSuperType(_typeInfo : ITypeInfo) : bool
    {
      throw NotImplementedException()
    }

    public override IsDerivedFrom(_fixedType : FixedType.Class) : bool
    {      
      throw NotImplementedException()
    }

    public override IsDerivedFrom(_typeInfo : ITypeInfo) : bool
    { 
      def typeInfo = _typeInfo :> CciTypeInfo;
      typeInfo._typeDef.BaseClasses.Any(t => t.InternedKey == _typeDef.InternedKey)
    }
    
    public override Extends        : FixedType.Class
    { 
      get
      {
        throw NotImplementedException()
      } 
    }
    public override Implements     : Seq[FixedType.Class] 
    {
      get
      {
        //_Eter_: этот код выдает все интерфейсы которые реализует данный тип (и базовые для них)        
        _typeDef.Interfaces.Select(i => Provider.GetType(i)).Cast.[FixedType.Class]()
      }
    }
    public override TypeParams     : Seq[ITypeParameterInfo] 
    { 
      [Memoize]
      get
      {
        _typeDef.GenericParameters.Select(tp => CciTypeParameterInfo(Provider, tp)).ToArray()
      }
    }

    [Memoize]
    GetNamespace() : string
    {
      match (_typeDef)
      {
        | nsType is INamespaceTypeReference => 
          match (nsType.ContainingUnitNamespace)
          {
            | unitNs is INestedUnitNamespaceReference => 
              TypeHelper.GetNamespaceName(unitNs, NameFormattingOptions.None)
                
            | _ => ""
          }
        | _ => ""
      }
    }
    
    public override Namespace : list[string]
    {
      [Memoize]
      get { GetNamespace().SplitToList('.') }
    }

    public override LookupMember(_name : string, _forCompletion : bool = false) : Seq[IMemberInfo]
    {
      throw NotImplementedException()
    }

    public override ConstructSubst(_tc : ITypeInfo, _typeParams : Seq[ITypeParameterInfo], _args : Seq[NType], _checkParams : bool) : Subst
    {
      throw NotImplementedException()
    }
    public override FreshSubst() : Subst
    {
     throw NotImplementedException()
    }
    public override MakeSubst(_parms : Seq[NType]) : Subst
    {
      throw NotImplementedException()
    }
    public override MakeUncheckedSubst(_parms : Seq[NType]) : Subst
    {
      throw NotImplementedException()
    }
    public override NestingSubst(_parent : ITypeInfo) : Subst
    {
      throw NotImplementedException()
    }
    
    
  }
}